package org.ndx.majick.ui;

import java.awt.GridLayout;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;

import org.ndx.majick.properties.Property;
import org.ndx.majick.properties.util.ClassMap;
import org.ndx.majick.properties.util.PropertiesFinder;
import org.ndx.majick.ui.object.ObjectPanelProvider;

/**
 * Generates UI for a provided bean.
 * @author Nicolas Delsaux
 *
 */
public class UIGenerator {
	/**
	 * Class internal logger. MUST NOT be used anywhere else, for logs readability
	 */
	private static final Logger logger = Logger.getLogger(UIGenerator.class
			.getName());
	/**
	 * Basic ui provider, to generate components ui.
	 * Don't get me wrong, there are some times when the mechanic behind properties is not that useful, and this is precisely such a case.
	 */
	private IUIProvider uiProvider;
	
	/**
	 * Finds properties in bean. This component
	 */
	private PropertiesFinder finder;
	
	/**
	 * Map associating classes to panel providers used to show them
	 */
	private ClassMap<IPanelProvider> panelProviders = new ClassMap<IPanelProvider>();

	public UIGenerator() {
		super();
		fillPanelProviders();
	}

	public UIGenerator(IUIProvider uiProvider, PropertiesFinder finder) {
		this();
		setUIProvider(uiProvider);
		setFinder(finder);
	}

	/** Default fill panel providers */
	private void fillPanelProviders() {
		put(Object.class, new ObjectPanelProvider());
	}

	public IUIProvider getUIProvider() {
		return uiProvider;
	}

	public void setUIProvider(IUIProvider uiProvider) {
		this.uiProvider = uiProvider;
	}

	public PropertiesFinder getFinder() {
		return finder;
	}

	public void setFinder(PropertiesFinder finder) {
		this.finder = finder;
	}

	/**
	 * Gets an editor panel for the provided bean. For that, the properties list of the bean is obtained by the {@link #finder}.
	 * Then, for each of the properties, the {@link #uiProvider} returns associated editor.
	 * Finally, editor component is given a presentation label and some decorations (like tooltip, and so on) and assembled. 
	 * @param object
	 * @return a panel allowing edition of the input bean. Notice this panel do not contains a "ok" or "close" button. However, 
	 * thanks to {@link UIProvider} mechanisms changes done to panel are directly reflected in model, and vice-versa.
	 */
	public JPanel getEditor(Object object) {
		Collection<Property<?>> properties = finder.getProperties(object);
		/* temporary store editors, in order to associate them labels */
		Map<Property<?>, JComponent> propertiesToEditors = getEditors(properties);
		return getPanel(object, properties, propertiesToEditors, IUIProvider.EDITOR);
	}

	public JPanel getViewer(Object object) {
		Collection<Property<?>> properties = finder.getProperties(object);
		/* temporary store editors, in order to associate them labels */
		Map<Property<?>, JComponent> propertiesToViewers = getViewers(properties);
		return getPanel(object, properties, propertiesToViewers, IUIProvider.VIEWER);
	}

	private JPanel getPanel(Object object, Collection<Property<?>> properties,
			Map<Property<?>, JComponent> propertiesToEditors, String mode) {
		/* also store labels */
		Map<Property<?>, JComponent> propertiesToLabels = getLabels(properties, mode);

		return getPanelProvider(object.getClass()).getPanel(mode, object, properties, propertiesToLabels,
				propertiesToEditors);
	}

	private IPanelProvider getPanelProvider(Class<? extends Object> class1) {
		return panelProviders.locate(class1);
	}

	private Map<Property<?>, JComponent> getLabels(
			Collection<Property<?>> properties, String mode) {
		Map<Property<?>, JComponent> propertiesToComponents = new LinkedHashMap<Property<?>, JComponent>();
		/* put editors in map */
		for(Property<?> prop : properties) {
			propertiesToComponents.put(prop, new JLabel(getText(prop, mode+".label")));
		}
		return propertiesToComponents;
	}

	/**
	 * Gets the text to put in property label
	 * @param prop input property
	 * @param type type of text to grab.
	 * @return the translated text, as returned by the TextProvider
	 */
	private String getText(Property<?> prop, String type) {
		return "ui"+type+"_"+prop.getName();
	}

	
	
	/**
	 * Gets the viewers map for the provided properties collection.
	 * Gets viewers from {@link UIProvider#getViewer(Property)} and put them in a map which keys are binded properties.
	 * @param properties list of properties
	 * @return
	 */
	private Map<Property<?>, JComponent> getViewers(
			Collection<Property<?>> properties) {
		Map<Property<?>, JComponent> propertiesToComponents = new LinkedHashMap<Property<?>, JComponent>();
		/* put editors in map */
		for(Property<?> prop : properties) {
			propertiesToComponents.put(prop, uiProvider.getViewer(prop));
		}
		return propertiesToComponents;
	}
	
	/**
	 * Gets the editors map for the provided properties collection.
	 * Gets editors from {@link UIProvider#getEditor(Property)} and put them in a map which keys are binded properties.
	 * @param properties list of properties
	 * @return
	 */
	private Map<Property<?>, JComponent> getEditors(Collection<Property<?>> properties) {
		Map<Property<?>, JComponent> propertiesToComponents = new LinkedHashMap<Property<?>, JComponent>();
		/* put editors in map */
		for(Property<?> prop : properties) {
			propertiesToComponents.put(prop, uiProvider.getEditor(prop));
		}
		return propertiesToComponents;
	}

	public IPanelProvider put(Class<?> key, IPanelProvider value) {
		return panelProviders.put(key, value);
	}
}
